//
//  GIFItem.swift
//  MySwiftPullToRefresh
//
//  Created by likai on 2017/5/4.
//  Copyright © 2017年 terry. All rights reserved.
//

import UIKit
import ImageIO
import MobileCoreServices

final class GIFItem{
    private let data: Data
    
    private let isBig: Bool
    
    private let height: CGFloat
    
    let imageView = GIFAnimatedImageView()
    
    init(data: Data , isBig: Bool, height:CGFloat ){
        self.data = data
        self.isBig = isBig
        self.height = height
        
        guard let animatedImage = GIFAnimatedImage(data: data) else {
            print("Error: data is not an animated image")
            return
        }
        imageView.animatedImage = animatedImage
        
        if isBig {
            imageView.bounds.size = CGSize(width: UIScreen.main.bounds.width, height: height)
            imageView.contentMode = .scaleAspectFill
        }else{
            let ratio = animatedImage.size.width / animatedImage.size.height
            imageView.bounds.size = CGSize(width: ratio * height * 0.67, height: height * 0.67)
            imageView.contentMode = .scaleAspectFit
        }
        
    }
    
    required init?(coder aDecoder: NSCoder){
        fatalError("init(coder:) has not been implemented")
        
    }
    
    func updateRefreshState(_ isRefreshing: Bool){
        isRefreshing ? imageView.startAnimating() : imageView.stopAnimating()
        
        
    }
    
    func updateProgress(_ progress: CGFloat){
        guard let count = imageView.animatedImage?.frameCount else {
            return
        }
        
        if progress == 1 {
            imageView.startAnimating()
        }else{
            imageView.stopAnimating()
            imageView.index = Int(CGFloat(count - 1) * progress)
        }
        
    }
    
}


// MARK: - GIFRefreshControl
// https://github.com/delannoyk/GIFRefreshControl
protocol AnimatedImage: class {
    var size: CGSize { get }
    var frameCount: Int { get }
    
    func frameDurationForImage(at index: Int) -> TimeInterval
    
    subscript(index: Int) -> UIImage { get } //subscript 通常情况下，我们在使用数组（Array）或字典（Dictionary）时会使用到下标。其实在Swift中，我们还可以给类、结构、枚举等自定义下标（subscript）。
}

class GIFAnimatedImage: AnimatedImage{
    //typealias就相当于objective-c中的typedef,就是将类型重命名,看起来更加语义化
    typealias imageInfo = (image: UIImage, duration: TimeInterval)
    private var images: [imageInfo] = []
    var size: CGSize{
  //UIImage.size  反映UIImage的大小。在iOS 4.0及更高版本中，这是以分数来衡量的。在3.x和更早版本中，以像素为单位
        /*
//        http://blog.csdn.net/sxh1234567/article/details/44884713
         
        第一反应可能就是image.size，恭喜你答错了，正确的答案是图像的实际的尺寸(像素)等于image.size * image.scale。如果做过界面贴图的话你可能经常会需要准备至少两套图，一套1倍图，一套图已@2x命名的二倍图。这样当我们的程序运行在retina屏幕的时候系统就会自动的去加载@2x的图片，它的size将和一倍图加载进来的size相等，但是scale却置为2，这点大家可以做个简单的小测试验证一下。然我们再深入一点儿为什么不直接加载到成二倍的尺寸呢，原因很简单因为我们在界面布局中逻辑坐标系中的(单位是point)，而实际的绘制都是在设备坐标系(单位是pixel)进行的，系统会自动帮我们完成从point到pixel之间的转化。其实这个比例也就刚好和UIScreen中的scale对应，这样整条scale的线就可以串通了。
         
         在使用Core Image中的滤镜效果时，需要注意，滤镜之后的UIImage的尺寸是图片的真实尺寸。
        */
        return images.first?.image.size ?? .zero
    }
    
    var frameCount: Int{
        return images.count
    }
    
    //初始化传入图片的Data数据,获取到了GIF图片信息数组(包含了UIImage和持续时间)
    init?(data:Data){
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) , let type = CGImageSourceGetType(source) else {
            return nil
        }
        
        let isTypeGIF = UTTypeConformsTo(type, kUTTypeGIF)//Returns whether a uniform type identifier conforms to another uniform type identifier. 这里实际上就是判断 根据 图片源 获取的类型 标示 是否是 GIF
        let count = CGImageSourceGetCount(source)// Returns the number of images (not including thumbnails) in the image source.返回图像源中的图像数量（不包括缩略图）
        if !isTypeGIF || count <= 1 {
            return nil
        }//必须是GIF类型 并且 图像数量大于1
        
        for index in 0 ..< count {
            guard let image = CGImageSourceCreateImageAtIndex(source, index, nil),//Creates a CGImage object for the image data associated with the specified index in an image source. 得到一个CGImage
            let info = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [String : Any],//A dictionary that contains the properties associated with the image. See CGImageProperties for a list of properties that can be in the dictionary.  包含与图像相关联的属性的字典。有关可以在字典中的属性列表，请参阅CGImageProperties。
            let gifInfo = info[kCGImagePropertyGIFDictionary as String] as? [String : Any] else {
                continue
            }
            var duration: Double = 0
            if let unclampedDelay = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? Double {
                //在动画序列中显示下一张图像之前等待的时间（以秒为单位）。该值可能为0毫秒或更高。 与kCGImagePropertyGIFDelayTime属性不同，此值不会被钳制在范围的低端。
                duration = unclampedDelay
            }else{
                //在动画序列中显示下一张图像之前等待的时间（以秒为单位）。
                duration = gifInfo[kCGImagePropertyGIFDelayTime as String] as? Double ?? 0.1
            }
            
            if duration <= 0.001 {
                duration = 0.1
            }
            
            images.append((UIImage(cgImage: image),duration))
            
        }
        
    }
    
    //获取到 GIF图数组 中的一个图片对应的时间长度
    func frameDurationForImage(at index: Int) -> TimeInterval {
        return images[index].duration
    }
    
    subscript(index: Int) -> UIImage{
        return images[index].image
    }
}


class GIFAnimatedImageView: UIImageView {
    var animatedImage: AnimatedImage?{
        didSet{
            image = animatedImage?[0]
        }
    }
    
    var index: Int = 0 {
        didSet{
            if index != oldValue{
                image = animatedImage?[index]
            }
        }
    }
    
    private var animated = false
    private var lastTimestampChange = CFTimeInterval(0)
    
    //CADisplayLink CADisplayLink对象是一个定时器对象，允许应用程序将其绘图同步到显示器的刷新率。
    /*
     您的应用程序初始化一个新的显示链接，提供一个目标对象和一个在屏幕更新时被调用的选择器。 要使显示循环与显示同步，您的应用程序会使用add（to：forMode :)方法将其添加到运行循环中。
     
     */
    private lazy var displayLink: CADisplayLink = {
        
        let displayLink = CADisplayLink(target: self, selector: #selector(refreshDispay))
        displayLink.add(to: .main, forMode: .commonModes)
        displayLink.isPaused = true
        
        return displayLink;
    }()
    
    override func startAnimating() {
        if animated {
            return
        }
        displayLink.isPaused = false
        animated = true
    }
    
    override func stopAnimating() {
        if !animated {
             return
        }
        displayLink.isPaused = true
        animated = false
    }
    
    //下面方法需要研究一下
    @objc private func refreshDispay(){
        guard animated, let animatedImage = animatedImage else {
            return
        }
        let currentFrameDuration = animatedImage.frameDurationForImage(at: index)
        let delta = displayLink.timestamp - lastTimestampChange
        //The time value associated with the last frame that was displayed.
        //The target should use the value of this property to calculate what should be displayed in the next frame
        //与显示的最后一帧相关联的时间值。目标应使用此属性的值来计算下一帧应显示的内容
        if delta >= currentFrameDuration {
            index = (index + 1) % animatedImage.frameCount
            lastTimestampChange = displayLink.timestamp
        }
    }
    
}
